package utils

import (
	"errors"
	"fmt"
	"math"
	"reflect"
	"sort"
)

// SlicePagination 表示包含所有必要元数据的分页结果。
type SlicePagination[T any] struct {
	Page        int  // 当前页
	PageSize    int  // 每页条数
	Total       int  // 总条数
	TotalPages  int  // 总页数
	Items       []T  // 当前页的数据
	HasPrevPage bool // 是否有上一页
	HasNextPage bool // 是否有下一页
}

// PaginateSlice 获取任何类型的切片、页码和页面大小，并返回 SlicePagination.
func PaginateSlice[T any](items []T, page, pageSize int) SlicePagination[T] {
	if page == 0 {
		page = 1
	}
	if pageSize == 0 {
		pageSize = 10
	}
	total := len(items)
	totalPages := int(math.Ceil(float64(total) / float64(pageSize)))
	start := (page - 1) * pageSize
	end := start + pageSize

	// 如果结束索引超过项目总数，则调整结束索引
	if end > total {
		end = total
	}

	// 如果 start 大于项目总数，则返回 Items 的空切片
	if start > total {
		start = total
	}

	return SlicePagination[T]{
		Page:        page,
		PageSize:    pageSize,
		Total:       total,
		TotalPages:  totalPages,
		Items:       items[start:end],
		HasPrevPage: page > 1,
		HasNextPage: page < totalPages,
	}
}

// CopySlice
// 复制切片为不同的引用（深拷贝）
// T为切片元素
func CopySlice[T any](original []T) []T {
	newSlice := make([]T, len(original))
	copy(newSlice, original)
	return newSlice
}

// SortStructs
// 对结构体切片进行排序
// T为结构体
func SortStructs[T any](slice []T, fieldName string, ascending bool) error {
	lessFunc, err := makeLessFunc(slice, fieldName)
	if err != nil {
		return err
	}

	sort.SliceStable(slice, func(i, j int) bool {
		vi := reflect.ValueOf(slice[i]).FieldByName(fieldName)
		vj := reflect.ValueOf(slice[j]).FieldByName(fieldName)
		if ascending {
			return lessFunc(vi.Interface(), vj.Interface())
		} else {
			return lessFunc(vj.Interface(), vi.Interface())
		}
	})
	return nil
}

func makeLessFunc[T any](slice []T, fieldName string) (func(value1, value2 any) bool, error) {
	if len(slice) == 0 {
		return nil, errors.New("slice is empty, can't determine the type of fieldName")
	}
	fieldValue := reflect.ValueOf(slice[0]).FieldByName(fieldName)
	if !fieldValue.IsValid() {
		return nil, fmt.Errorf("field %s not found in the struct", fieldName)
	}

	switch fieldValue.Interface().(type) {
	case int:
		return func(value1, value2 any) bool {
			return value1.(int) < value2.(int)
		}, nil
	case int8:
		return func(value1, value2 any) bool {
			return value1.(int8) < value2.(int8)
		}, nil
	case int16:
		return func(value1, value2 any) bool {
			return value1.(int16) < value2.(int16)
		}, nil
	case int32:
		return func(value1, value2 any) bool {
			return value1.(int32) < value2.(int32)
		}, nil
	case int64:
		return func(value1, value2 any) bool {
			return value1.(int64) < value2.(int64)
		}, nil
	case uint:
		return func(value1, value2 any) bool {
			return value1.(uint) < value2.(uint)
		}, nil
	case uint8:
		return func(value1, value2 any) bool {
			return value1.(uint8) < value2.(uint8)
		}, nil
	case uint16:
		return func(value1, value2 any) bool {
			return value1.(uint16) < value2.(uint16)
		}, nil
	case uint32:
		return func(value1, value2 any) bool {
			return value1.(uint32) < value2.(uint32)
		}, nil
	case uint64:
		return func(value1, value2 any) bool {
			return value1.(uint64) < value2.(uint64)
		}, nil
	case float32:
		return func(value1, value2 any) bool {
			return value1.(float32) < value2.(float32)
		}, nil
	case float64:
		return func(value1, value2 any) bool {
			return value1.(float64) < value2.(float64)
		}, nil
	case string:
		return func(value1, value2 any) bool {
			return value1.(string) < value2.(string)
		}, nil
	default:
		return nil, errors.New("unsupported type for comparison")
	}
}
